﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using CustomProvider;
using CustomProvider.Event;
using ServerSide.Model;

namespace ServerSide.Communication
{
    /// <summary>
    /// The class for communication with the clients
    /// The server build with an asynchronous socket, so the server is not suspended while it waits for a connection from client.
    /// </summary>
    public class AsynSocket
    {
        #region Fields

        /// <summary>
        /// Notifies one or more waiting thread to proceed
        /// </summary>
        public AutoResetEvent _autoDoneEvent = new AutoResetEvent(false);

        /// <summary>
        /// Socket to listen connection from clients
        /// </summary>
        private Socket _socListener;

        /// <summary>
        /// The list of worker sockets
        /// </summary>
        private Dictionary<string, Socket> _allWorkerSockets = new Dictionary<string, Socket>();

        /// <summary>
        /// The list of worker sockets which are not in STARTED status yet
        /// </summary>
        private List<string> _freeWorkerSockets = new List<string>();

        /// <summary>
        /// The list of worker sockets which are in STARTED status yet
        /// </summary>
        private List<Dictionary<string, bool>> _busyWorkerSockets = new List<Dictionary<string, bool>>();

        /// <summary>
        /// Checking Socket has been closed for listening
        /// </summary>
        private bool _isClosedSocketListener = false;

        /// <summary>
        /// Constain constants of this class
        /// </summary>
        private static class Const
        {
            /// <summary>
            /// Size of receive buffer.
            /// </summary>
            public const int BufferSize = 1024;

            /// <summary>
            /// The maximum length of the pending connections queue.
            /// </summary>
            public const int BackLog = 100;

            /// <summary>
            /// The message ESTABLISH_COMMUNICATION which is sent from the client
            /// </summary>
            public const string ESTABLISH_COMMUNICATION_MSG = "ESTABLISH_COMMUNICATION";

            /// <summary>
            /// The message REJECTCONNECT which is sent to the client
            /// </summary>
            public const string REJECTCONNECT_MSG = "REJECTCONNECT";

            /// <summary>
            /// The message DISCONNECT which is sent from the client
            /// </summary>
            public const string DISCONNECT_MSG = "DISCONNECT";

            /// <summary>
            /// The message STARTGAME which is sent to the client
            /// </summary>
            public const string STARTGAME_MSG = "STARTGAME";

            /// <summary>
            /// The message GUESS which is sent from the client
            /// </summary>
            public const string GUESS_MSG = "GUESS";

            /// <summary>
            /// The message GUESSRESULT which is sent to the client
            /// </summary>
            public const string GUESSRESULT_MSG = "GUESSRESULT";

            /// <summary>
            /// The message ENDGAME which is sent from the client
            /// </summary>
            public const string ENDGAME_MSG = "ENDGAME";

            /// <summary>
            /// The message OPPONENTDISCONNECT which is sent to the client
            /// </summary>
            public const string OPPONENTDISCONNECT_MSG = "OPPONENTDISCONNECT";

            /// <summary>
            /// The message PLAYAGAIN which is sent from the client
            /// </summary>
            public const string PLAYAGAIN_MSG = "PLAYAGAIN";

            /// <summary>
            /// The message UPDATE_ALREADY_PLAYERLIST which is sent to the client
            /// </summary>
            public const string UPDATE_ALREADY_PLAYERLIST_MSG = "UPDATE_ALREADY_PLAYERLIST";

            /// <summary>
            /// The message INVITETOPLAY which is sent from the client
            /// </summary>
            public const string INVITETOPLAY_MSG = "INVITETOPLAY";

            /// <summary>
            /// The message REPLY_INVITETOPLAY which is sent to the server
            /// </summary>
            public const string REPLY_INVITETOPLAY_MSG = "REPLY_INVITETOPLAY";

            /// <summary>
            /// The message REJECT_INVITETOPLAY which is sent from the client
            /// </summary>
            public const string REJECT_INVITETOPLAY_MSG = "REJECT_INVITETOPLAY";

            /// <summary>
            /// The message GAMEFINISHED which is sent to the client
            /// </summary>
            public const string GAMEFINISHED_MSG = "GAMEFINISHED";
        }

        #endregion

        #region Methods

        /// <summary>
        /// Start to listen connection from the clients
        /// </summary>
        /// <param name="ServerAddress">The address of the server</param>
        /// <param name="ServerPort">The port on server which is opened for clients connect to</param>
        public void StartListening(string ServerAddress, int ServerPort)
        {
            try
            {
                this.NotifyDisplayMessageOnGUI("Server start listening");

                this._isClosedSocketListener = false;
                // Data buffer for incoming data.
                byte[] bytes = new Byte[Const.BufferSize];

                // Establish the local endpoint for the socket.
                IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(ServerAddress), ServerPort);
                // Create a TCP/IP socket.
                this._socListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this._socListener.Bind(localEndPoint);
                this._socListener.Listen(Const.BackLog);

                while (true)
                {
                    // Start an asynchronous socket to listen for connections.
                    this.NotifyDisplayMessageOnGUI("Waiting for a connection...");
                    this._socListener.BeginAccept(new AsyncCallback(AcceptCallback), this._socListener);

                    // Wait until a connection is made before continuing.
                    this._autoDoneEvent.WaitOne();

                    // Breaking loop to stop listening
                    if (this._isClosedSocketListener)
                        return;
                }
            }
            catch (Exception ex)
            {
                this.NotifyDisplayMessageOnGUI(string.Format("Error on Communication: {0}", ex.ToString()));
            }
        }

        /// <summary>
        /// The method is invoked when the server accepted a connection from any client
        /// </summary>
        /// <param name="ar"></param>
        public void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                if (this._isClosedSocketListener)
                    return;

                // Notify the main thread to continue to listening connection from clients
                this._autoDoneEvent.Set();

                // Get the socket that handles the client request.
                Socket listener = (Socket)ar.AsyncState;
                Socket handler = listener.EndAccept(ar);
                if (handler.Connected == false)
                    return;

                // Create the state object.
                StateObject state = new StateObject();
                state.WorkSocket = handler;

                // Begins to asynchronously receive data from a Socket.
                // state: a user-defined object which is passed to the invoke method when the operation is completed
                handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
            }
            catch (Exception ex)
            {
                this.NotifyDisplayMessageOnGUI(string.Format("Error on AcceptCallback: {0}", ex.ToString()));
            }
        }

        /// <summary>
        /// The method is invoked when the server received messages from clients
        /// </summary>
        /// <param name="ar"></param>
        public void ReadCallback(IAsyncResult ar)
        {
            try
            {
                if (this._isClosedSocketListener)
                    return;

                string content = string.Empty;
                // Retrieve the state object and the handler from the asynchronous state object.
                StateObject state = (StateObject)ar.AsyncState;
                Socket handler = state.WorkSocket;
                if (handler.Connected == false)
                    return;

                // Read data from the client socket. 
                int bytesRead = handler.EndReceive(ar);

                if (bytesRead > 0)
                {
                    // There  might be more data, so store the data received so far.
                    state.ReceivedMsg.Append(Encoding.ASCII.GetString(state.Buffer, 0, bytesRead));
                    content = state.ReceivedMsg.ToString();
                    this.NotifyDisplayMessageOnGUI(string.Format("Received message from client : {0}", content));          
                    // Clear buffer
                    state.ReceivedMsg.Clear();

                    Action<bool> actionToProceed = (ToProceed) =>
                    {
                        if (ToProceed == false)
                        {
                            // Reject connection and close client Socket
                            this.NotifyDisplayMessageOnGUI(string.Format("Close Socket: {0}", handler.LocalEndPoint.ToString())); 
                            this.CloseSocket(handler);
                            return;
                        }
                        // Ready to receive the next data
                        handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
                    };
                    // Handle received message from the Client
                    this.MessageFromClientsHandler(handler, content, actionToProceed);
                }
            }
            catch (Exception ex)
            {
                this.NotifyDisplayMessageOnGUI(string.Format("Error on ReadCallback: {0}", ex.ToString()));
            }
        }

        /// <summary>
        /// The handler for handling received message from the clients
        /// </summary>
        /// <param name="handler">The socket to communicate with the Client</param>
        /// <param name="ReceivedMsg">The message which was sent from the client</param>
        /// <param name="ActionToProceed">The action will be invoked when method is completed</param>
        private void MessageFromClientsHandler(Socket Handler, string ReceivedMsg, Action<bool> ActionToProceed)
        {
            // Remove end of string
            var msgComp = ReceivedMsg.Split(new char[] { ':' });
            var msgType = msgComp[0];
            var msgContent = msgComp[1];

            switch (msgType)
            {
                case Const.ESTABLISH_COMMUNICATION_MSG:
                    this.MessageEstablishCommunicationHandler(Handler, msgContent, ActionToProceed);
                    break;

                case Const.DISCONNECT_MSG:
                    this.MessageDisConnectHandler(Handler, msgContent, ActionToProceed);
                    break;

                case Const.GUESS_MSG:
                    this.MessageGuessHandler(Handler, msgContent, ActionToProceed);
                    break;

                case Const.ENDGAME_MSG:
                    this.MessageEndGameHandler(Handler, msgContent, ActionToProceed);
                    break;

                case Const.PLAYAGAIN_MSG:
                    this.MessagePlayAgainHandler(Handler, msgContent, ActionToProceed);
                    break;

                case Const.INVITETOPLAY_MSG:
                    this.MessageInviteToPlayHandler(Handler, msgContent, ActionToProceed);
                    break;

                case Const.REPLY_INVITETOPLAY_MSG:
                    this.MessageReplyInviteToPlayHandler(Handler, msgContent, ActionToProceed);
                    break;
            }
        }

        /// <summary>
        /// Handle message reply of invitation to play from the client
        /// </summary>
        /// <param name="Handler"></param>
        /// <param name="ContentMsg"></param>
        /// <param name="ActionToProceed"></param>
        private void MessageReplyInviteToPlayHandler(Socket Handler, string ContentMsg, Action<bool> ActionToProceed)
        {
            var compContentMsg = ContentMsg.Split(new char[] { '#' });
            var idPlayerSend = compContentMsg[0];
            var idPlayerReceive = compContentMsg[1];
            var sResult = compContentMsg[2];

            var result = bool.Parse(sResult);
            if (result)
            {
                var dctWorker = new Dictionary<string, bool>();
                dctWorker.Add(idPlayerSend, false);
                dctWorker.Add(idPlayerReceive, false);
                // Update busy workers list
                this._busyWorkerSockets.Add(dctWorker);
                this._freeWorkerSockets.Remove(dctWorker.Keys.ElementAt(0));
                this._freeWorkerSockets.Remove(dctWorker.Keys.ElementAt(1));

                // Send message to another clients update Already player list
                this.NotifyUpdateAlreadyPlayerList();

                // Wait for sent next messages
                var autoEvent = new AutoResetEvent(false);
                autoEvent.WaitOne(500);

                // Send STARTGAME
                var subject = WordList.GetInstance().GetRandom(WordList.GetInstance().Subjects);
                var word = subject.GetRandom(subject.Words);
                var rand = new Random();
                var idxAllowTurn = rand.Next(0, 2);

                foreach (string idPlayer in dctWorker.Keys)
                {
                    var player = this._allWorkerSockets[idPlayer];
                    // Send question to Client
                    var sendMsg = string.Format("{0}:{1}#{2}#{3}#{4}#{5}#{6}",
                        Const.STARTGAME_MSG, subject.Name, word.Suggestion, word.Answer.Length,
                        dctWorker.Keys.ElementAt(0), dctWorker.Keys.ElementAt(1),
                        dctWorker.Keys.ElementAt(idxAllowTurn));
                    this.SendMsgAsync(player, sendMsg);
                }
            }
            else
            {
                // Forward the invitation has been rejected to sender
                var playerTobeForwarded = this._allWorkerSockets[idPlayerSend];
                this.SendMsgAsync(playerTobeForwarded, Const.REJECT_INVITETOPLAY_MSG);
            }

            // Proceed to receive the next data
            ActionToProceed(true);
        }

        /// <summary>
        /// Handle message invite to play from the client
        /// </summary>
        /// <param name="Handler"></param>
        /// <param name="ContentMsg"></param>
        /// <param name="ActionToProceed"></param>
        private void MessageInviteToPlayHandler(Socket Handler, string ContentMsg, Action<bool> ActionToProceed)
        {
            var compContentMsg = ContentMsg.Split(new char[] { '#' });
            var idPlayerSend = compContentMsg[0];
            var idPlayerReceive = compContentMsg[1];

            // Forward the invitation to the receiver
            var playerTobeForwarded = this._allWorkerSockets[idPlayerReceive];
            this.SendMsgAsync(playerTobeForwarded, string.Format("{0}:{1}#{2}", Const.INVITETOPLAY_MSG, idPlayerSend, idPlayerReceive));

            // Proceed to receive the next data
            ActionToProceed(true);
        }

        /// <summary>
        /// Handle play gain message from the clients
        /// </summary>
        /// <param name="Handler"></param>
        /// <param name="ContentMsg"></param>
        /// <param name="ActionToProceed"></param>
        private void MessagePlayAgainHandler(Socket Handler, string ContentMsg, Action<bool> ActionToProceed)
        {
            var compContentMsg = ContentMsg.Split(new char[] { '#' });
            var idPlayer = compContentMsg[0];
            var oldSubject = compContentMsg[1];
            var oldSuggestion = compContentMsg[2];

            // Send answer result to the Clients
            var pairPlaying = this.GetPairPlaying(idPlayer);
            if (pairPlaying != null)
            {
                pairPlaying[idPlayer] = true;
                if (pairPlaying.All(n => n.Value))
                {
                    pairPlaying[pairPlaying.ElementAt(0).Key] = false;
                    pairPlaying[pairPlaying.ElementAt(1).Key] = false;
                    this.PlayAgain(oldSubject, oldSuggestion, pairPlaying);
                }
            }

            // Proceed to receive the next data
            ActionToProceed(true);
        }

        /// <summary>
        /// Handle message end game from the client
        /// </summary>
        /// <param name="Handler"></param>
        /// <param name="ContentMsg"></param>
        /// <param name="ActionToProceed"></param>
        private void MessageEndGameHandler(Socket Handler, string ContentMsg, Action<bool> ActionToProceed)
        {
            var compContentMsg = ContentMsg.Split(new char[] { '#' });
            var idWinner = compContentMsg[0];

            // Send answer result to the Clients
            var pairPlaying = this.GetPairPlaying(idWinner);
            if (pairPlaying != null)
            {
                foreach (string idPlayer in pairPlaying.Keys)
                {
                    var player = this._allWorkerSockets[idPlayer];
                    this.SendMsgSync(player, string.Format("{0}:{1}", Const.GAMEFINISHED_MSG, idWinner));
                }
            }

            this.NotifyEndGame(idWinner);
            // Proceed to receive the next data
            ActionToProceed(true);
        }

        /// <summary>
        /// Handle message guess from the clients
        /// </summary>
        /// <param name="Handler"></param>
        /// <param name="ContentMsg"></param>
        /// <param name="ActionToProceed"></param>
        private void MessageGuessHandler(Socket Handler, string ContentMsg, Action<bool> ActionToProceed)
        {
            var compContentMsg = ContentMsg.Split(new char[] { '#' });
            var playerID = compContentMsg[0];
            var character = compContentMsg[1];
            var subject = compContentMsg[2];
            var suggestion = compContentMsg[3];
            var answer = WordList.GetInstance().Subjects.First(n => n.Name.Equals(subject)).Words.First(n2 => n2.Suggestion.Equals(suggestion)).Answer;
            var sendMsg = string.Empty;

            if (answer.ToUpper().Contains(character.ToUpper()))
                sendMsg = string.Format("{0}:{1}#{2}#{3}#{4}", Const.GUESSRESULT_MSG, playerID, character, "true", answer.BuildSearchedCharacters(character));
            else
                sendMsg = string.Format("{0}:{1}#{2}#{3}#{4}", Const.GUESSRESULT_MSG, playerID, character, "false", string.Empty);

            // Send answer result to the Clients
            var pairPlaying = this.GetPairPlaying(playerID);
            if (pairPlaying != null)
            {
                foreach (string idPlayer in pairPlaying.Keys)
                {
                    var player = this._allWorkerSockets[idPlayer];
                    this.SendMsgSync(player, sendMsg);
                }
            }
            
            // Proceed to receive the next data
            ActionToProceed(true);
        }

        /// <summary>
        /// Handle message disconnect from the clients
        /// </summary>
        /// <param name="handler">The socket to communicate with the Client</param>
        /// <param name="ReceivedMsg">The content message which was sent from the client</param>
        /// <param name="ActionToProceed">The action will be invoked when method is completed</param>
        private void MessageDisConnectHandler(Socket Handler, string ContentMsg, Action<bool> ActionToProceed)
        {
            var msgComp = ContentMsg.Split(new char[] { '@' });
            var playerName = msgComp[0];
            var playerIP = msgComp[1];
            var idPlayer = ContentMsg;

            // Update the list of worker
            this._allWorkerSockets.Remove(idPlayer);
            this._freeWorkerSockets.Remove(idPlayer);

            var pairPlaying = this.GetPairPlaying(idPlayer);
            if (pairPlaying != null)
            {
                var otherPlayer = pairPlaying.FirstOrDefault(n => n.Key.Equals(idPlayer) == false);
                this.SendMsgAsync(this._allWorkerSockets[otherPlayer.Key], string.Format("{0}:{1}", Const.OPPONENTDISCONNECT_MSG, idPlayer));
                this._busyWorkerSockets.Remove(pairPlaying);
            }

            // Display message on GUI and update layer list
            var sendMsg = string.Format("The player {0} at IP Address {1} has been disconnected", playerName, playerIP);
            this.NotifyDisplayMessageOnGUI(sendMsg);
            this.NotifyDisConnectStatus(playerName, playerIP);
            
            // Wait for sent next messages
            var autoEvent = new AutoResetEvent(false);
            autoEvent.WaitOne(500);

            // Update already player list
            this.NotifyUpdateAlreadyPlayerList();

            // Not proceed to receive the next data
            ActionToProceed(false);
        }

        /// <summary>
        /// Handle message connect from the clients
        /// </summary>
        /// <param name="handler">The socket to communicate with the Client</param>
        /// <param name="ReceivedMsg">The content message which was sent from the client</param>
        /// <param name="ActionToProceed">The action will be invoked when method is completed</param>
        private void MessageEstablishCommunicationHandler(Socket Handler, string ContentMsg, Action<bool> ActionToProceed)
        {
            var msgComp = ContentMsg.Split(new char[] { '@' });
            var playerName = msgComp[0];
            var playerIP = msgComp[1];
            var idPlayer = ContentMsg;

            // Checking whether the new player is valid
            if (this._allWorkerSockets.ContainsKey(idPlayer))
            {
                // Send message to the Client to notify the connection has been rejected
                this.SendMsgSync(Handler, Const.REJECTCONNECT_MSG);

                var autoEvent = new AutoResetEvent(false);
                autoEvent.WaitOne(500);

                // Sent message to the Client about the reason for rejection
                var sendMsg = string.Format("There is an player with the same Name {0} and IP Address {1}, you cannot connect to the Sever", playerName, playerIP);
                this.SendMsgSync(Handler, sendMsg);

                // Not proceed to receive the next data
                ActionToProceed(false);
            }
            else
            {
                // Notify to confirm that the client has been established commnunication
                // And update player list on GUI
                var sendMsg = string.Format("The player {0} at IP Address {1} has been connected", playerName, playerIP);
                this.SendMsgAsync(Handler, sendMsg);
                this.NotifyConnectStatus(playerName, playerIP);

                // Update the list of worker
                this._allWorkerSockets.Add(idPlayer, Handler);
                this._freeWorkerSockets.Add(idPlayer);

                // Wait for sent next messages
                var autoEvent = new AutoResetEvent(false);
                autoEvent.WaitOne(500);

                // Update already player list
                this.NotifyUpdateAlreadyPlayerList();

                // Invoke action to receive the next data
                ActionToProceed(true);
            }
        }

        /// <summary>
        /// Notify to update already player list
        /// </summary>
        private void NotifyUpdateAlreadyPlayerList()
        {
            // Send message to connected clients to update already player list
            var sPlayers = this.ConvertFromListToString(this._freeWorkerSockets);
            foreach (Socket socket in this._allWorkerSockets.Values)
            {
                var msg = string.Format("{0}:{1}", Const.UPDATE_ALREADY_PLAYERLIST_MSG, sPlayers);
                this.SendMsgAsync(socket, msg);
            }
        }

        /// <summary>
        /// Get pair of player playing from a specifed player
        /// </summary>
        /// <param name="idPlayer"></param>
        /// <returns></returns>
        private Dictionary<string, bool> GetPairPlaying(string idPlayer)
        {
            // Select pair of player playing
            Func<Dictionary<string, bool>, bool> selector = (lst) =>
            {
                return lst.Any(n => n.Key.Equals(idPlayer));
            };
            return this._busyWorkerSockets.FirstOrDefault(selector);
        }

        /// <summary>
        /// Build a string from list item
        /// </summary>
        /// <param name="lst"></param>
        /// <returns></returns>
        private string ConvertFromListToString(List<string> lst)
        {
            var s = string.Empty;
            for (int i = 0; i < lst.Count; i++)
            {
                if (i < lst.Count - 1)
                    s += lst[i] + ",";
                else
                    s += lst[i];
            }
            return s;
        }

        /// <summary>
        /// Notify the Clients that game is start
        /// </summary>
        /// <param name="oldSubject">The previous subject</param>
        /// <param name="oldSuggestion">The previous suggestion</param>
        /// <param name="pairPlaying">The previous players</param>
        private void PlayAgain(string oldSubject, string oldSuggestion, Dictionary<string, bool> pairPlaying)
        {
            Subject subject = null;
            Word word = null;

            // Get random subject that is not same with the previous one
            if (WordList.GetInstance().Subjects.Count > 1)
                subject = WordList.GetInstance().GetRandom(WordList.GetInstance().Subjects.Where(n => n.Name.NonEquals(oldSubject)).ToList());
            else
                subject = WordList.GetInstance().GetRandom(WordList.GetInstance().Subjects);

            // Get random suggestion that is not same with the previous one
            if (subject.Words.Count > 1)
                word = subject.GetRandom(subject.Words.Where(n => n.Suggestion.NonEquals(oldSuggestion)).ToList());
            else
                word = subject.GetRandom(subject.Words);
            
            var rand = new Random();
            var idxAllowTurn = rand.Next(0, 2);

            foreach (string idPlayer in pairPlaying.Keys)
            {
                var socket = this._allWorkerSockets[idPlayer];
                // Send question to Client
                var sendMsg = string.Format("{0}:{1}#{2}#{3}#{4}#{5}#{6}",
                    Const.STARTGAME_MSG, subject.Name, word.Suggestion, word.Answer.Length,
                    pairPlaying.Keys.ElementAt(0), pairPlaying.Keys.ElementAt(1),
                    pairPlaying.Keys.ElementAt(idxAllowTurn));
                this.SendMsgSync(socket, sendMsg);
            }
        }

        /// <summary>
        /// Using worker socket to send synchronouse message to clients
        /// </summary>
        /// <param name="Handler">The Worker Socket which is created to work with the specified client</param>
        /// <param name="Msg">The message to be sent to client</param>
        private void SendMsgSync(Socket Handler, string Msg)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(Msg);
            // Begin sending the data to the remote device.
            Handler.Send(byteData);
            // Dislay message on GUI
            this.NotifyDisplayMessageOnGUI(Msg);
        }

        /// <summary>
        /// Using worker socket to send asynchronouse message to clients
        /// </summary>
        /// <param name="Handler">The Worker Socket which is created to work with the specified client</param>
        /// <param name="Msg">The message to be sent to client</param>
        private void SendMsgAsync(Socket Handler, string Msg)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(Msg);

            Action<IAsyncResult> actionSendCallback = (ar) =>
            {
                // Retrieve the socket from the state object.
                Socket handler = (Socket)ar.AsyncState;
                // Complete sending the data to the clients.
                int bytesSent = handler.EndSend(ar);
            };

            // Begin sending the data to the remote device.
            Handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(actionSendCallback), Handler);

            // Dislay message on GUI
            this.NotifyDisplayMessageOnGUI(Msg);
        }

        /// <summary>
        /// Close client Socket
        /// </summary>
        /// <param name="Handler">The Worker Socket which is created to work with the specified client</param>
        private void CloseSocket(Socket Handler)
        {
            // Close worker socket
            Handler.Close();
            Handler = null;

            // Notify to other clients can be connected to the server
            this._autoDoneEvent.Set();
        }

        /// <summary>
        /// Close all sockets to stop listening
        /// </summary>
        public void CloseSockets()
        {
            if (this._socListener != null)
            {
                this._isClosedSocketListener = true;
                // Close Socket and release associated resources
                this._socListener.Close();
                this._autoDoneEvent.Set();
            }

            foreach (Socket socket in this._allWorkerSockets.Values)
                socket.Close();

            this._allWorkerSockets.Clear();
            this.NotifyDisplayMessageOnGUI("The server has been stop listening");
        }

        /// <summary>
        /// Send message to the Clients
        /// </summary>
        /// <param name="msg"></param>
        public void SendMsg(string msg)
        {
            foreach (Socket socket in this._allWorkerSockets.Values)
                this.SendMsgSync(socket, msg);
        }

        /// <summary>
        /// Notify End Game
        /// </summary>
        /// <param name="idWinner"></param>
        private void NotifyEndGame(string idWinner)
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyEndGameEvent>().Publish(idWinner);
        }

        /// <summary>
        /// Display message for users
        /// </summary>
        /// <param name="Msg">The message will be displayed</param>
        private void NotifyDisplayMessageOnGUI(string Msg)
        {
            Msg = DateTime.Now.ToString() + " " + Msg;
            EventAggregatorFactory.Instance.GetEvent<NotifyDisplayMessageOnGUIEvent>().Publish(Msg);
        }

        /// <summary>
        /// Notify the connection status
        /// </summary>
        /// <param name="IsConnected">Determines the connection has been established</param>
        /// <param name="MsgTxt">Messagte text</param>
        /// <param name="Name">PLayer Name</param>
        /// <param name="IPAddress">IPAddress of player</param>
        private void NotifyConnectStatus(string Name = "", string IPAddress = "")
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyConnectStatusEvent>().Publish(
                new NotifyConnectStatusEvent.ConnectParam(true, string.Empty, new NotifyConnectStatusEvent.PlayerInfo(Name, IPAddress)));
        }

        /// <summary>
        /// Notify the connection status
        /// </summary>
        /// <param name="IsConnected">Determines the connection has been established</param>
        /// <param name="MsgTxt">Messagte text</param>
        /// <param name="Name">PLayer Name</param>
        /// <param name="IPAddress">IPAddress of player</param>
        private void NotifyDisConnectStatus(string Name = "", string IPAddress = "")
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyConnectStatusEvent>().Publish(
                new NotifyConnectStatusEvent.ConnectParam(false, string.Empty, new NotifyConnectStatusEvent.PlayerInfo(Name, IPAddress)));
        }


        #endregion
    }

    /// <summary>
    /// State object for reading client data asynchronously
    /// </summary>
    public class StateObject
    {
        /// <summary>
        /// Client  socket.
        /// </summary>
        public Socket WorkSocket = null;

        /// <summary>
        /// Size of receive buffer.
        /// </summary>
        public const int BufferSize = 1024;

        /// <summary>
        /// Receive buffer.
        /// </summary>
        public byte[] Buffer = new byte[BufferSize];

        /// <summary>
        /// Received data string.
        /// </summary>
        public StringBuilder ReceivedMsg = new StringBuilder();
    }
}
